
#include <BFile>
#include <BSource>
#include <BApplication>

#include "member_BSource.h"
#include "member_BScript.h"
#include "member_BObject.h"

using namespace BWE;

#define member					(*(member_BSource*)_ptr)
#define member_allocate()		_ptr = new member_BSource(this)
#define member_release()		delete (member_BSource*)_ptr

BSource::BSource(BScript* script)
{
	member_allocate();
	if (script)
		script->insertSource(this);
}
BSource::BSource(const BString& name, BScript* script) : BObject(name)
{
	member_allocate();
	if (script)
		script->insertSource(this);
}
BSource::~BSource()
{
	member_release();
}

void BSource::copy(const BSource* other)
{
	object_member(this)->name = object_member(other)->name;
	member.fileName = source_member(other)->fileName;
	member.text = source_member(other)->text;
}
BSource* BSource::clone() const
{
	BSource* source = new BSource();
	source->copy(this);
	return source;
}

bool BSource::load(const BString& fileName)
{
	if (BFile::Load(fileName, member.text))
	{
		if (member.fileName.empty())
		{
			member.fileName = fileName;
			if (this->checks(Signal_FileNameChanged))
				this->emit(Signal_FileNameChanged, fileName);
		}
		this->emit(Signal_TextChanged, member.text);
		this->emit(Signal_Dirty);
		return true;
	}
	return false;
}
bool BSource::save(const BString& fileName)
{
	if (BFile::Save(fileName, member.text))
	{
		if (member.fileName.empty())
		{
			member.fileName = fileName;
			if (this->checks(Signal_FileNameChanged))
				this->emit(Signal_FileNameChanged, fileName);
		}
		return true;
	}
	return false;
}

bool BSource::setFileName(const BString& fileName)
{
	if (member.fileName != fileName)
	{
		member.fileName = fileName;
		if (this->checks(Signal_FileNameChanged))
			this->emit(Signal_FileNameChanged, fileName);
		return true;
	}
	return false;
}
const BString& BSource::fileName() const
{
	return member.fileName;
}

bool BSource::setText(const BString& text)
{
	if (member.text != text)
	{
		member.text = text;
		object_member(this)->check_emit(Signal_TextChanged, text);
		return true;
	}
	return false;
}
const BString& BSource::text() const
{
	return member.text;
}

bool BSource::analyse()
{
	member.cachedFileMap.reset();

	member.error = false;
	if (BString* message = member.message)
		message->reset();
	member.levels.reset();
	member.stack.reset();
	member.srcnames.reset();
	member.document.reset();
	member.includes.reset();
	member.declares.reset();
	member.enumMap.reset();
	member.classMap.reset();
	member.functionMap.reset();

	BString path = member.fileName.path();
	if (path.size())
	{
		AutoWorkPath workpath(path);
		if (workpath.failed())
		{
			member.errorMessage() << "Enter path failed : " << path;
			member.raiseMessage(Signal_Message);
			return false;
		}
	}

	if (member.text.size())
	{
		member.srcnames += this->name();
		BString::const_iterator it = member.text.begin();
		int line = 0;
		while (it.valid())
		{
			member.tempTypes.clear();
			if (BExp* exp = member.parse(member.text, it, line))
			{
				member.checkExp(exp);
				if (exp->syntax == Syntax_Decl || exp->syntax == Syntax_Array)
				{
					for (int i = 0; i < member.declares.size(); i++)
					{
						BExp* decl = member.declares[i];
						if (decl->name == exp->name)
						{
							member.errorMessage(exp) << exp->name << " is already declared.";
							member.raiseMessage(Signal_Message);
							return false;
						}
					}
					member.declares.append(exp);
				}
				member.organizeLevel(exp);
			}
		}
		member.srcnames -= this->name();
	}
	return member.error == false;
}
const BExpHolderArray& BSource::document() const
{
	return member.document;
}
void BSource::clean()
{
	member.cachedFileMap.clear();
	member.error = false;
	member.message.clear();
	member.levels.clear();
	member.stack.clear();
	member.srcnames.clear();
	member.document.clear();
	member.includes.clear();
	member.declares.clear();
	member.enumMap.clear();
	member.classMap.clear();
	member.functionMap.clear();
}

const BClass* BSource::findClass(const BString& type)
{
	if (type.endWith('&') || type.beginWith("const "))
	{
		BString temp = type;
		temp.remove("const ");
		temp -= '&';
		return findClass(temp);
	}
	if (BClass* cls = member.classMap(type))
		return cls;
	if (member.script)
		return  member.script->findClass(type);
	return 0;
}

BScript* BSource::script()
{
	return member.script;
}
const BScript* BSource::script() const
{
	return member.script;
}
